// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-02-01 // using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Linq; using System.Text; using System.Xml.Linq; using JetBrains.Annotations; using LargoCommon.Abstract; using LargoCommon.Midi; namespace LargoCommon.Music { /// /// Musical Body. /// public class MusicalBody { #region Fields /// /// Musical Bars. /// private List bars; #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The given context. public MusicalBody(MusicalContext givenContext) { this.Context = givenContext; } /// /// Initializes a new instance of the class. /// /// The given strip. public MusicalBody(MusicalStrip givenStrip) { this.Context = givenStrip.Context; this.Context.Header.NumberOfLines = givenStrip.Lines.Count; this.PrepareElements(givenStrip); } #endregion #region Properties - Xml /// Gets Xml representation. /// Property description. public virtual XElement GetXElement { get { XElement xbody = new XElement("Body"); //// Bars XElement xbars = new XElement("Bars"); //// Musical Bars to XML foreach (MusicalBar mbar in this.Bars.Where(mbar => mbar != null)) { //// mtrack.MusicalBlock = givenMusicalBlock; var xbar = mbar.GetXElement; xbars.Add(xbar); } //// var xtrack = MusicalTrackPort.WriteMusicalTrack(givenElement.Track); xelement.Add(xtrack); xbody.Add(xbars); return xbody; } } #endregion #region Properties /// /// Gets or sets the context. /// /// /// The context. /// public MusicalContext Context { get; set; } /// Gets list of bars. /// Property description. public IList Bars { get { Contract.Ensures(Contract.Result>() != null); return this.bars ?? (this.bars = new List()); } //// private set { this.musicalBars = value; } } /// /// Gets or sets the tempo events. /// /// /// The tempo events. /// public IEnumerable TempoEvents { get; set; } //// private /// Gets string with harmony. /// General musical property. /// Returns value. [UsedImplicitly] public string HarmonyToString { get { var s = new StringBuilder(); s.Append(" Bar Modality Harmony(Duration) \n"); s.Append("-------------------------------------------------------------------\n"); var bs = from b in this.Bars orderby b.BarNumber select b; foreach (var bar in bs.Where(bar => bar != null && !bar.IsEmpty)) { s.Append(bar.NumberToString()); s.Append(bar.HarmonicBar.ModalityToString); s.Append(bar.PureChordsToString + "\n"); } return s.ToString(); } } /// Gets string of clusters. /// General musical property. /// Returns value. [UsedImplicitly] public string ClustersToString { get { var s = new StringBuilder(); s.Append(" Bar Cluster Harmony (Modality) \n"); s.Append("-------------------------------------------------------------------\n"); var bs = from b in this.Bars orderby b.BarNumber select b; foreach (var bar in bs.Where(bar => bar != null && !bar.IsEmpty)) { s.Append(bar.ClustersToString + "\n"); } return s.ToString(); } } /// /// Gets the editor elements. /// /// /// The editor elements. /// public IList AllElements { get { var list = new List(); foreach (var bar in this.Bars) { list.AddRange(bar.Elements); } //// var selectedList = (from element in list orderby element.Point.BarNumber, //// element.Point.LineIndex select element).ToList(); return list; } } /// /// Gets the line chunks. /// /// /// The line chunks. /// [UsedImplicitly] public IList LineChunks { get { var list = new List(); for (int trackIdx = 0; trackIdx < this.Context.Header.NumberOfLines; trackIdx++) { var elem = this.FistPlayingElementInTrack(trackIdx); if (elem == null) { continue; } var instrument = elem.Status.Instrument; //// if (instrument == null) { instrument = elem.Line.Status.Instrument; } var lineChunk = new PackLine( this.Context.Header, elem.Status.LineType, LineRhythm.HarmonicShape, instrument, elem.Status.Octave, elem.Status.Loudness, elem.Status.MelodicFunction, elem.Status.MelodicShape, 1); list.Add(lineChunk); } return list; } } #endregion #region Public static /// /// Sets the purpose to elements. /// /// The given purpose. /// The given elements. public static void SetPurposeToElements(LinePurpose givenPurpose, IEnumerable givenElements) { foreach (var element in givenElements) { if (element?.Status != null) { if (givenPurpose == LinePurpose.Fixed && (!element.MusicalLine.HasContent || element.MusicalLine.IsEmpty)) { continue; } element.Status.LocalPurpose = givenPurpose; } } } /// /// Sets the instrument to elements. /// /// The given instrument. /// The given elements. public static void SetInstrumentToElements(MusicalInstrument givenInstrument, IEnumerable givenElements) { foreach (var element in givenElements) { if (element?.Status != null) { //// if (element.MusicalLine.IsEmpty)) { continue; } element.Status.Instrument = givenInstrument; } } } /// /// Sets the octave to elements. /// /// The given octave. /// The given elements. public static void SetOctaveToElements(MusicalOctave givenOctave, IEnumerable givenElements) { foreach (var element in givenElements) { if (element?.Status != null) { //// if (element.MusicalLine.IsEmpty)) { continue; } element.Status.Octave = givenOctave; } } } /// /// Sets the loudness to elements. /// /// The given loudness. /// The given elements. public static void SetLoudnessToElements(MusicalLoudness givenLoudness, IEnumerable givenElements) { foreach (var element in givenElements) { if (element?.Status != null) { //// if (element.MusicalLine.IsEmpty)) { continue; } element.Status.Loudness = givenLoudness; } } } #endregion #region Rhythmic /// /// Rhythmic of harmony. /// /// Returns value. [UsedImplicitly] public IList RhythmicOfHarmony() { var structs = new List(); // ReSharper disable once LoopCanBeConvertedToQuery foreach (var bar1 in this.Bars.Where(bar1 => bar1 != null)) { if (bar1.HarmonicBar == null) { continue; } var rstruct = bar1.HarmonicBar.RhythmicStructure; //// Clone? structs.Add(rstruct); } return structs; } /// /// Reorders the line rhythmic. /// /// The line number. /// The block model. [UsedImplicitly] public void SetRandomRhythm(int lineIndex, RhythmicModel model) { Contract.Requires(model != null); var elements = this.ElementsOfLine(lineIndex); var master = new ElementMaster(elements); var structures = model.RhythmicStructuresOfMotives; master.SetRandomRhythm(structures); } #endregion #region Reorder line properties /// /// Reorders the line rhythmic. /// /// The line number. public void ReorderLineRhythmic(int lineIndex) { var elements = this.ElementsOfLine(lineIndex); var master = new ElementMaster(elements); master.ReorderRhythmic(); } /// /// Reorders the line rhythmic. /// /// The line number. public void ReorderLineMelodic(int lineIndex) { var elements = this.ElementsOfLine(lineIndex); //// var musicalEditorElements = elements as IList ?? elements.ToList(); var master = new ElementMaster(elements); master.ReorderMelodic(); } /// /// Reorders the line rhythmic. /// public void ReorderLineHarmonic() { foreach (var bar1 in this.Bars) { if (bar1?.HarmonicBar == null) { continue; } var bar1C = bar1; foreach (var bar2 in this.Bars.Where(bar2 => bar2.HarmonicBar != null && bar2.BarNumber > bar1C.BarNumber).Where(bar2 => MathSupport.RandomNatural(10) < 3)) { bar1.SwapHarmonyWith(bar2); } } } #endregion #region String representation /// String representation of the object. /// Returns value. public override string ToString() { var s = new StringBuilder(); s.AppendFormat("MusicalBody (Length {0})", this.Bars.Count); return s.ToString(); } #endregion /// /// Gets the sorted bars. /// /// Type of the line. /// Returns value. /// /// The sorted bars. /// public IList SortedBars(MusicalLineType lineType) { Contract.Ensures(Contract.Result>() != null); var bs = this.Bars.ToList(); foreach (var bar in bs) { bar.ResetValuesOfTicks(); } bs.Sort(new BarComparer(lineType)); return bs; } #region Public methods - Elements /// /// Marks the rhythmic motive. /// /// The given motive. /// The given area. public void MarkRhythmicMotive(RhythmicMotive givenMotive, MusicalArea givenArea) { for (int barNumber = givenArea.StartPoint.BarNumber; barNumber <= givenArea.EndPoint.BarNumber; barNumber++) { var bar = this.GetBar(barNumber); if (givenArea.StartPoint.LineIndex >= bar?.Elements.Count) { ////2019/12 (?) continue; } var element = bar?.Elements[givenArea.StartPoint.LineIndex]; if (element?.Status == null) { continue; } element.Status.RhythmicMotive = givenMotive; } } /// /// Marks the melodic motive. /// /// The given motive. /// The given area. public void MarkMelodicMotive(MelodicMotive givenMotive, MusicalArea givenArea) { for (int barNumber = givenArea.StartPoint.BarNumber; barNumber <= givenArea.EndPoint.BarNumber; barNumber++) { var bar = this.GetBar(barNumber); if (givenArea.StartPoint.LineIndex >= bar?.Elements.Count) { ////2019/12 (?) continue; } var element = bar?.Elements[givenArea.StartPoint.LineIndex]; if (element?.Status == null) { continue; } element.Status.MelodicMotive = givenMotive; } } /// /// Gets the bar. /// /// The given bar number. /// Returns value. public MusicalBar GetBar(int givenBarNumber) { var barIdx = givenBarNumber - 1; if (barIdx < 0 || barIdx >= this.Bars.Count) { return null; } var bar = this.Bars[barIdx]; return bar; } /// /// Prepares the rhythm in line. /// /// Index of the given line. public void PrepareRhythmInLine(int givenLineIndex) { foreach (var bar in this.Bars) { var point = MusicalPoint.GetPoint(givenLineIndex, bar.BarNumber); var me = this.GetElement(point); if (me == null) { continue; } if (me.Status.RhythmicStructure != null) { me.Status.RhythmicMotive = RhythmicMotive.SimpleRhythmicMotive(me.Status.RhythmicStructure); //// me.Status.RhythmicMotive = me.Status.RhythmicMotive; } me.FillWithRequestedRhythm(); } } /// /// Prepares the elements. /// /// The given strip. public void PrepareElements(MusicalStrip givenStrip) { var h = this.Context.Header; var numberOfTracks = h.NumberOfLines; //// 2016 givenStrip. Lines .Count; var numberOfBars = h.NumberOfBars; var barMidiDuration = MusicalProperties.BarMidiDuration( h.System.RhythmicOrder, h.Metric.MetricBeat, h.Metric.MetricGround, h.Division); MusicalBar lastBar = null; for (int barNumber = 1; barNumber <= numberOfBars; barNumber++) { MusicalBar bar = new MusicalBar(barNumber, this) { BarNumber = barNumber, PreviousBar = lastBar, TimePoint = (barNumber - 1) * barMidiDuration }; bar.SetElements(new List()); if (lastBar != null) { lastBar.NextBar = bar; } for (byte lineIndex = 0; lineIndex < numberOfTracks; lineIndex++) { var line = givenStrip.Lines.ElementAt(lineIndex); var element = new MusicalElement(line.FirstStatus, null) { Bar = bar, Line = line }; element.SetTones(line.MusicalTonesInBar(barNumber)); element.Status.BarNumber = barNumber; //// 2019/01 element.Status.MelodicVariety = line.FirstStatus.MelodicVariety; //// 2019/01 element.Status.MelodicPlan = new MelodicPlan(element.Tones); //// 2016/10 !?!?!?!? if (!element.Tones.HasAnySoundingTone) { element.Status.LocalPurpose = LinePurpose.None; } bar.Elements.Add(element); } this.Bars.Add(bar); lastBar = bar; } } /// /// Sets the purpose to all elements. /// /// The given purpose. public void SetPurposeToAllElements(LinePurpose givenPurpose) { MusicalBody.SetPurposeToElements(givenPurpose, this.AllElements); } /// /// Gets the element. /// /// The given point. /// /// Returns value. /// public MusicalElement GetElement(MusicalPoint givenPoint) { MusicalElement element = null; var barIndex = givenPoint.BarNumber - 1; if (barIndex >= 0 && barIndex < this.Bars.Count) { var bar = this.Bars[barIndex]; var lineIndex = givenPoint.LineIndex; if (lineIndex >= 0 && lineIndex < bar.Elements.Count) { element = bar.Elements[lineIndex]; } } return element; } /// /// Editors the elements of line. /// /// The line number. /// Returns value. public IEnumerable ElementsOfLine(int lineIndex) { var list = (from v in this.AllElements where v.Point.LineIndex == lineIndex select v).ToList(); //// var selectedList = (from element in list orderby element.Point.BarNumber //// select element).ToList(); return list; } /// /// Elements the of line. /// /// The track identifier. /// Returns value. public IEnumerable ElementsOfLine(Guid lineIdent) { var list = (from v in this.AllElements where v.Line.LineIdent == lineIdent select v).ToList(); //// var selectedList = (from element in list orderby element.Point.BarNumber //// select element).ToList(); return list; } /// Enumerates rhythmic structures of line in this collection. /// The track identifier. /// /// An enumerator that allows foreach to be used to process rhythmic structures of line in this /// collection. /// [UsedImplicitly] public IEnumerable RhythmicStructuresOfLine(Guid lineIdent) { var list = (from v in this.AllElements where v.Line.LineIdent == lineIdent && v.Status?.RhythmicStructure != null select v.Status.RhythmicStructure).ToList(); //// var selectedList = (from element in list orderby element.Point.BarNumber //// select element).ToList(); return list; } /// /// Clones the line elements. /// /// The line from. /// The line to. [UsedImplicitly] public void CopyLineStatus(int lineFrom, int lineTo) { foreach (var bar in this.Bars) { var pointFrom = MusicalPoint.GetPoint(lineFrom, bar.BarNumber); var elementFrom = this.GetElement(pointFrom); var pointTo = MusicalPoint.GetPoint(lineTo, bar.BarNumber); var elementTo = this.GetElement(pointTo); elementTo.Status = (LineStatus)elementFrom.Status.Clone(); } } /// /// Determines whether [is in editor] [the specified line]. /// /// Index of the line. /// The bar. /// /// true if [is in editor] [the specified line]; otherwise, false. /// [UsedImplicitly] public bool IsInEditor(int lineIndex, int bar) { return lineIndex >= 0 && bar >= 1 && lineIndex < this.Context.Header.NumberOfLines && bar <= this.Context.Header.NumberOfBars; } /// /// Alters the octaves. /// /// The line number. public void RandomAlterOctaves(int lineIndex) { var elements = this.ElementsOfLine(lineIndex); //// int n = 0; foreach (var element in elements.Where(element => element != null)) { element.RandomAlterOctave(); } } #endregion #region Public methods - Lines /// /// Deletes the track. /// /// The line number. public void DeleteLine(int lineIndex) { foreach (var bar in this.Bars) { //// .Where(bar => bar != null) if (lineIndex >= bar.Elements.Count) { continue; } var element = bar.Elements[lineIndex]; bar.Elements.Remove(element); } } /// /// Deletes the line. /// /// The line identifier. public void DeleteLine(Guid lineIdent) { foreach (var bar in this.Bars) { var element = bar.GetElement(lineIdent); if (element != null) { bar.Elements.Remove(element); } } } #endregion #region Public methods - Bars /// /// Creates bars according to harmonic definition in the given segment. /// /// The changes. public void SetHarmonicBasis(MusicalChanges changes) { //// bool relative if (changes == null) { return; } var harmonicOrder = this.Context.Header.System.HarmonicOrder; //// var templateStatus = new BarStatus(); int barNumberInMotive = 0; foreach (var bar in this.Bars.Where(bar => bar != null && harmonicOrder > 0)) { //// Musical block has harmonic background (so it is not rhythmic only) if (changes.CurrentChange(0, bar.BarNumber, MusicalChangeType.Harmonic) is HarmonicChange dc) { bar.ApplyHarmonicChange(dc); } //// var ms = (BarStatus)templateStatus.Clone(); //// ms.BarNumber = bar.BarNumber; //// ms.LineIndex = 0; if (bar.HasHarmonicMotive) { barNumberInMotive = bar.BarNumber - bar.HarmonicStatus.BarNumber + 1; var harmonicBar = bar.HarmonicStatus.HarmonicMotive.HarmonicBarWithNumber( barNumberInMotive, this.Context.Header.System); var newHarmonicBar = (HarmonicBar)harmonicBar.Clone(); newHarmonicBar.BarNumber = bar.BarNumber; bar.SetHarmonicBar(newHarmonicBar); bar.NumberInHarmonicMotive = barNumberInMotive; if (barNumberInMotive == 1 && bar.PreviousBar != null) { bar.PreviousBar.IsLastInHarmonicMotive = true; } } else { //// throw new ArgumentException("No har.motive bar!"); var hmb1 = new HarmonicBar(barNumberInMotive, bar.BarNumber); bar.SetHarmonicBar(hmb1); } //// 2016 this.BarStatus = ms; } foreach (var bar in this.Bars.Where(bar => bar != null)) { bar.SetModalityToStructures(MusicalSettings.Singleton.MinimalModalityLevel); } } #endregion #region Public methods - Tempo /// /// Set Tempo Events From. /// /// Midi time from. /// Midi time To. /// The given tone tracks. public void SetTempoEventsFrom(long timeFrom, long timeTo, List givenToneTracks) { if (givenToneTracks.Count == 0) { return; } var tempoEvents = new List(); var track0 = givenToneTracks[0]; if (track0 == null) { return; } var originalEvents = track0.Sequence.GetTempoEvents(timeFrom, timeTo); if (originalEvents != null) { IEnumerable midiEvents = originalEvents as IList ?? originalEvents.ToList(); if (midiEvents.Any()) { tempoEvents.AddRange(midiEvents); } } //// Remove duplicity events var selectedTempoEvents = new List(); MetaTempo lastEvent = null; foreach (var ev in tempoEvents) { var tev = ev as MetaTempo; if (lastEvent != null && tev != null && tev.StartTime == lastEvent.StartTime && tev.Tempo == lastEvent.Tempo) { continue; } selectedTempoEvents.Add(ev); lastEvent = tev; } this.TempoEvents = selectedTempoEvents; this.SetTempoStatusFromEvents(selectedTempoEvents); } #endregion #region Public methods - Rhythm /// /// Sets the line simple rhythm. /// /// The line number. /// The rhythmic order. public void SetSimpleRhythm(int lineIndex, byte rhythmicOrder) { var elements = this.ElementsOfLine(lineIndex); if (elements == null) { return; } var lineElements = elements as IList ?? elements.ToList(); var rsystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, rhythmicOrder); const decimal rhythmNumber = 1; var rstruct = new RhythmicStructure(rsystem, rhythmNumber); foreach (var element1 in lineElements.Where(element1 => element1 != null)) { element1.Status.RhythmicStructure = rstruct; } } /// /// Sets the line rhythm to bars. /// /// Index of the line. /// The rhythmic structure. [UsedImplicitly] public void SetRhythmToBars(int lineIndex, RhythmicStructure rhythmicStructure) { var lineElements = this.ElementsOfLine(lineIndex); var elements = lineElements as IList ?? lineElements.ToList(); foreach (var element1 in elements.Where(element1 => element1 != null)) { element1.Status.RhythmicStructure = rhythmicStructure; } } /// /// Sets the simple rhythm to selected bars. /// /// The line number. /// The target rhythmic order. /// Period in bars. public void SetSimpleRhythmToBars(int lineIndex, byte targetRhythmicOrder, int period) { var lineElements = this.ElementsOfLine(lineIndex); var elements = lineElements as IList ?? lineElements.ToList(); var rsystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, targetRhythmicOrder); const decimal simpleRhythm = 1; var rstruct = new RhythmicStructure(rsystem, simpleRhythm); RhythmicStructure lastDefinedStruct = rstruct; foreach (var element1 in elements.Where(element1 => element1 != null)) { if (element1.Status.RhythmicStructure != null) { lastDefinedStruct = element1.Status.RhythmicStructure; } element1.Status.RhythmicStructure = element1.Point.BarNumber % period == 0 ? rstruct : lastDefinedStruct; } } /// /// Sets the rhythm to bars. /// /// Index of the line. /// The line rhythm. /// The target rhythmic order. public void SetRhythmToBars(int lineIndex, LineRhythm lineRhythm, byte targetRhythmicOrder) { switch (lineRhythm) { case LineRhythm.SimpleOneTone: { this.SetSimpleRhythmToBars(lineIndex, targetRhythmicOrder, 8); } break; case LineRhythm.HarmonicShape: { this.SetHarmonicRhythmToBars(lineIndex, 0, targetRhythmicOrder); //// 2 = combination } break; case LineRhythm.HarmonicStructure: { this.SetHarmonicRhythmToBars(lineIndex, 1, targetRhythmicOrder); //// 2 = combination } break; } } /// /// Sets the line harmonic rhythm to bars. /// /// The line number. /// The variant. /// The target rhythmic order. public void SetHarmonicRhythmToBars(int lineIndex, byte variant, byte targetRhythmicOrder) { var lineElements = this.ElementsOfLine(lineIndex); var musicalEditorElements = lineElements as IList ?? lineElements.ToList(); var targetSystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, targetRhythmicOrder); foreach (var element1 in musicalEditorElements.Where(element1 => element1 != null)) { var bar = this.Bars[element1.Point.BarNumber - 1]; var rs0 = bar.HarmonicBar?.RhythmicStructure; RhythmicStructure rhyStructure = null; if (variant == 0 && rs0 != null) { rhyStructure = rs0; } if (rhyStructure == null) { bar.DetermineCommonRhythmicShape(true); var rs1 = bar.CommonRhythmicStructure(); if (variant == 1 || variant == 0) { rhyStructure = rs1; } else { if (rs1.ToneLevel > 5 && rs0 != null) { rhyStructure = rs0; continue; } else { rhyStructure = rs1; } } } var targetRhythmicStructure = rhyStructure.ConvertToSystem(targetSystem); element1.Status.RhythmicStructure = targetRhythmicStructure; //// element1.Status.RhythmicStructure = rs0 != null && rs0.ToneLevel > 3 ? rs1 : rs0; //// element1.Status.RhythmicStructure = MathSupport.RandomNatural(10) < 5 ? rs0 : rs1; } } #endregion #region Status /// /// Sets the melodic status from tones. /// [UsedImplicitly] public void SetStatusFromMusic() { var changes = this.ExtractTempoChanges(); if (changes != null) { var tempoChanges = changes.ToList(); TempoChange tempoStatus = null; foreach (var bar in this.Bars) { var tempoChange = (from c in tempoChanges where c.BarNumber == bar.BarNumber select c) .FirstOrDefault(); if (tempoChange != null) { tempoStatus = tempoChange; } if (tempoStatus == null) { continue; } bar.ApplyTempoChange(tempoStatus); } } for (int lineIndex = 0; lineIndex < this.Context.Header.NumberOfLines; lineIndex++) { foreach (var bar in this.Bars) { var element = (from elem in bar.Elements where elem.Line != null && elem.Line.LineIndex == lineIndex select elem).FirstOrDefault(); //// var element = bar.Elements[lineIndex]; //// !!!!!!!!! attention! //// Rhythmical and melodical structure in bar if (element == null) { continue; } element.Status.BarNumber = bar.BarNumber; element.Status.BeatLevel = (byte)element.Tones.Count; element.Status.ToneLevel = (byte)element.Tones.CountOfMelTones; var musicalTonesInBar = element.Tones; if (musicalTonesInBar == null || musicalTonesInBar.Count == 0) { continue; } //// Rhythmical structure in bar - from tones RhythmicStructure rstruct = musicalTonesInBar.DetermineRhythmicStructure(this.Context.Header.System.RhythmicOrder); element.Status.RhythmicStructure = rstruct; //// Melodic structure in bar - from tones var isMelodic = element.Status.IsMelodic; if (isMelodic) { var melodicTonesInBar = element.SingleMelodicTones(); if (melodicTonesInBar != null && melodicTonesInBar.Any()) { //// lastMelodicTone var mstruct = melodicTonesInBar.DetermineMelodicStructure(null, bar.HarmonicBar); if (mstruct == null) { var system = new MelodicSystem(1, 1); mstruct = new MelodicStructure(system, 1); } element.Status.MelodicStructure = mstruct; } } if (element.Status.LineType == MusicalLineType.Rhythmic) { element.Status.Instrument = new MusicalInstrument(musicalTonesInBar.FirstRhythmicInstrument); } if (element.Status.LineType == MusicalLineType.Melodic) { element.Status.Instrument = new MusicalInstrument(musicalTonesInBar.FirstMelodicInstrument); } } } } /// /// Extracts the simple changes. /// public void SetBodyStatusFromTones() { var setup = this.Context.Settings; this.SetHarmonicStatusFromTones(3, setup.FullHarmonization); //// Status of tracks for (int lineIndex = 0; lineIndex < this.Context.Header.NumberOfLines; lineIndex++) { foreach (var bar in this.Bars) { var element = bar.Elements[lineIndex]; //// 2018/10 Only fixed tracks!? //// 2018/10 element.Line.Status.LocalPurpose if (element.Status.LocalPurpose == LinePurpose.Fixed) { element.SetElementStatusFromTones(); } } } } /// /// Set Harmonic Status From Tones. /// /// The given maximum tones in chord. /// if set to true [given full harmonization]. public void SetHarmonicStatusFromTones(byte givenMaxTonesInChord, bool givenFullHarmonization) { var numberOfBars = Math.Min(this.Context.Header.NumberOfBars, MusicalSettings.Singleton.MaxNumberOfBars); this.Context.Header.NumberOfBars = numberOfBars; var harmonicStreamAnalyzer = new HarmonicStreamAnalyzer( this.Context.Header, givenMaxTonesInChord, givenFullHarmonization) { SharpChordEdges = true, HarmonicSpace = new HarmonicSpace(this.Context.Header.System.HarmonicSystem) { ConsiderEnergyDecreaseInTime = false, ConsiderImpulseBindings = false, StrongImpulseBindings = false, ConsiderContinuityBindings = false } }; foreach (MusicalBar bar in this.Bars) { var harmonicBar = harmonicStreamAnalyzer.DetermineHarmonyInBar(bar); //// var newHarmonicBar = (HarmonicBar)harmonicBar.Clone(); //// bar.HarmonicBar = newHarmonicBar; bar.SetHarmonicBar(harmonicBar); bar.HarmonicBar.BarNumber = bar.BarNumber; } } #endregion #region Changes /// /// Extracts the simple changes. /// /// Type of the given change. /// /// Returns object. /// public IEnumerable ExtractSimpleChanges(MusicalChangeType givenChangeType) { var changes = new Collection(); for (int lineIndex = 0; lineIndex < this.Context.Header.NumberOfLines; lineIndex++) { var lastOctave = -10; var lastLoudness = -10; var lastStaff = -10; var lastVoice = -10; var lastToneLevel = -10; var lastBeatLevel = -10; var lastRhythmicTension = -10; byte lastInstrument = (byte)MidiMelodicInstrument.None; foreach (var bar in this.Bars) { if (bar.Elements.Count <= lineIndex) { //// 2017/03 !?!?! continue; } var element = bar.Elements[lineIndex]; if (element.Line == null) { continue; } //// var isMelodic = element.Status.IsMelodic; AbstractChange change; //// *** Loudness *** if (givenChangeType == MusicalChangeType.Loudness || givenChangeType == MusicalChangeType.All) { var loudness = element.Status.Loudness; if ((int)loudness != lastLoudness) { change = new LoudnessChange(bar.BarNumber, element.Line.LineIndex) { LoudnessBase = loudness }; changes.Add(change); lastLoudness = (int)loudness; } } //// *** Energy *** if (givenChangeType == MusicalChangeType.Energy || givenChangeType == MusicalChangeType.All) { var rstruct = element.Status.RhythmicStructure; if (rstruct != null && (rstruct.ToneLevel != lastToneLevel || rstruct.Level != lastBeatLevel || (byte)rstruct.FormalBehavior.Variance != lastRhythmicTension)) { change = new EnergyChange(bar.BarNumber, element.Line.LineIndex) { ToneLevel = rstruct.ToneLevel, BeatLevel = rstruct.Level, RhythmicTension = (byte)rstruct.FormalBehavior.Variance }; changes.Add(change); lastToneLevel = rstruct.ToneLevel; lastBeatLevel = rstruct.Level; lastRhythmicTension = (byte)rstruct.FormalBehavior.Variance; } } //// *** StaffChange *** if (givenChangeType == MusicalChangeType.Staff || givenChangeType == MusicalChangeType.All) { var staff = element.Status.Staff; var voice = element.Status.Voice; if (staff != lastStaff || voice != lastVoice) { change = new StaffChange(bar.BarNumber, element.Line.LineIndex) { Staff = staff, Voice = voice }; changes.Add(change); } lastStaff = staff; lastVoice = voice; } //// *** Octaves *** if (givenChangeType == MusicalChangeType.Octave || givenChangeType == MusicalChangeType.All) { var octave = element.Status.Octave; if ((int)octave != lastOctave) { change = new OctaveChange(bar.BarNumber, element.Line.LineIndex) { MusicalOctave = octave, MusicalBand = MusicalProperties.BandTypeFromOctave(octave) }; changes.Add(change); lastOctave = (int)octave; } } //// *** Instrument *** if (givenChangeType == MusicalChangeType.Instrument || givenChangeType == MusicalChangeType.All) { if (element.Status.Instrument == null) { element.Status.Instrument = new MusicalInstrument(MidiMelodicInstrument.None); } var instrument = element.Status.Instrument.Number; var channel = element.Line.MainVoice.Channel; if (instrument != lastInstrument) { change = new InstrumentChange(bar.BarNumber, element.Line.LineIndex) { Instrument = element.Status.Instrument, Channel = channel }; changes.Add(change); } lastInstrument = instrument; } } } return changes; } #endregion #region Private methods /// /// Extracts the tempo changes. /// /// /// Returns value. /// private IEnumerable ExtractTempoChanges() { //// cyclomatic complexity 10:15 //// var tracks = musicalBlock.MidiFile.AllTracks(); //// IEnumerable tempoEvents = tracks.GetTempoEvents(); var tempoEvents = this.TempoEvents; //// Music Xml if (tempoEvents == null) { return null; } var tempoChanges = new List(); int lastTempo = 0, lastBarNumber = 0; var header = this.Context.Header; foreach (var ev in tempoEvents) { MetaTempo tempoEvent = ev as MetaTempo; if (tempoEvent == null) { continue; } var currentTempo = tempoEvent.Tempo; if (currentTempo > 0 && currentTempo != lastTempo) { var metricGround = MusicalProperties.GetMetricGround(header.Metric.MetricBase); var barDivision = MusicalProperties.BarDivision(header.Division, header.Metric.MetricBeat, metricGround); var barNumber = (int)Math.Floor((double)tempoEvent.StartTime / barDivision) + 1; if (barNumber > lastBarNumber) { var change = new TempoChange(barNumber) { TempoNumber = currentTempo }; tempoChanges.Add(change); lastBarNumber = barNumber; } } lastTempo = currentTempo; } return tempoChanges; } /// /// Fists playing element in track. /// /// Index of the line. /// /// Returns value. /// private MusicalElement FistPlayingElementInTrack(int lineIdx) { MusicalElement element = null; foreach (var bar in this.Bars) { var elem = bar.Elements[lineIdx]; if (elem?.Status == null || !elem.Status.HasInstrument) { continue; } element = elem; break; } return element; } #endregion /// /// Sets the tempo status from events. /// /// The tempo events. private void SetTempoStatusFromEvents(List tempoEvents) { //// 2019/02 taken from ConvertStripToBody var h = this.Context.Header; var barMidiDuration = MusicalProperties.BarMidiDuration(h.System.RhythmicOrder, h.Metric.MetricBeat, h.Metric.MetricGround, h.Division); if (tempoEvents != null) { var midiEvents = tempoEvents as IList; if (midiEvents.Any()) { int currentTempoNumber = 0; foreach (var bar in this.Bars) { //// tev.BarNumber == bar.BarNumber ? if ((from tev in midiEvents where tev.StartTime <= bar.TimePoint && bar.TimePoint <= tev.StartTime + barMidiDuration select tev).FirstOrDefault() is MetaTempo metaTempo) { currentTempoNumber = metaTempo.Tempo; } bar.TempoNumber = currentTempoNumber; } } } } #region Sorting (internal only) /// /// Enables comparison of two events based on delta times. /// /// private sealed class BarComparer : IComparer { /// /// The line type /// private readonly MusicalLineType lineType; /// /// Initializes a new instance of the class. /// /// Type of the given line. public BarComparer(MusicalLineType givenLineType) { this.lineType = givenLineType; } #region Implementation of IComparer /// /// Compares two MidiEvents based on delta times. /// /// The first MidiEvent to compare. /// The second MidiEvent to compare. /// /// Returns -1 if x.StartTime is larger, 0 if they're the same, 1 otherwise. /// /// /// x /// or /// y /// public int Compare(MusicalBar x, MusicalBar y) { var barX = x; var barY = y; // Make sure they're valid if (barX == null) { throw new ArgumentNullException(nameof(x)); } if (barY == null) { throw new ArgumentNullException(nameof(y)); } // Compare the delta times return barX.ValueOfTicks(this.lineType).CompareTo(barY.ValueOfTicks(this.lineType)); //// CountOfTones } #endregion } #endregion } }